#define LOG_TAG "call_manager"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))

#include <jni.h>
#include <string>
#include <android/log.h>
#include <unistd.h>

/**
 * TODO 动态注册
*/

/**
 * 对应java类的全路径名，.用/代替
 */
const char *classPathName = "com/newlink/building/library_rino/RinoCallManager";

//Java虚拟机实例指针
JavaVM *gJavaVM = NULL;

//RinoCallManager源码结构体
typedef struct {
    jclass clazz;
    jobject globalObject;
    jweak weakObject;

    jmethodID rinoEventCallback;
} RinoCallManagerClassInfo;

static RinoCallManagerClassInfo rinoCallManagerClassInfo;

//jni方法结构体
typedef struct jniMethod {
    const char *className;
    const char *methodName;
    const char *methodType;
    jmethodID *jmethod;
} jniMethod_t;


JNIEnv *get_env(JavaVM *vm, int *attach) {
    if (vm == NULL) return NULL;
    JNIEnv *jni_env = NULL;
    int status = vm->GetEnv((void **) &jni_env, JNI_VERSION_1_6);

    if (status == JNI_EDETACHED || jni_env == NULL) {
        status = vm->AttachCurrentThread(&jni_env, NULL);
        if (status < 0) {
            jni_env = NULL;
        } else {
            *attach = 1;
        }
    }
    return jni_env;
}

/**
 * java上层的统一回调函数
 */
void rinoEventCallback(int32_t type, int32_t state, char *msg) {
    LOGD("%s: rinoEventCallback: state:%d msg:%s", __FUNCTION__, state, msg);
    int attach = 0;
    JNIEnv *env = get_env(gJavaVM, &attach);
    jstring message = NULL;
    message = env->NewStringUTF((const char *) msg);
    env->CallVoidMethod(rinoCallManagerClassInfo.globalObject,
                        rinoCallManagerClassInfo.rinoEventCallback,
                        type, state, message);
    if (NULL != message) {
        env->DeleteLocalRef(message);
    }
}

//发起通话请求
extern "C" JNIEXPORT jint JNICALL
nativeTakeInvite(JNIEnv *env, jobject obj, jstring jdevId, jboolean isVideoEnabled) {
    char *devId = (NULL != jdevId) ? (char *) env->GetStringUTFChars(jdevId, NULL) : NULL;
    LOGD("%s: devId: %s isVideoEnabled: :%d", __FUNCTION__, devId, isVideoEnabled);
    rinoEventCallback(1, 0, "Invite");
    //模拟3s后自动接听
    usleep(3000 * 1000);
    rinoEventCallback(1, 1, "Connected");
    return 1;
}


extern "C" JNIEXPORT jint JNICALL
nativeRegisterCallModule(JNIEnv *env, jobject obj, jstring jdevId) {
    char *devId = (NULL != jdevId) ? (char *) env->GetStringUTFChars(jdevId, NULL) : NULL;
    LOGD("%s: devId: %s", __FUNCTION__, devId);
    return 1;
}

extern "C" JNIEXPORT jint JNICALL
nativenotifyAcceptInvite(JNIEnv *env, jobject obj, jstring jdevId) {
    char *devId = (NULL != jdevId) ? (char *) env->GetStringUTFChars(jdevId, NULL) : NULL;
    LOGD("%s: devId: %s", __FUNCTION__, devId);
    return 1;
}

extern "C" JNIEXPORT jint JNICALL
nativeNotifyRingBack(JNIEnv *env, jobject obj, jstring jdevId) {
    char *devId = (NULL != jdevId) ? (char *) env->GetStringUTFChars(jdevId, NULL) : NULL;
    LOGD("%s: devId: %s", __FUNCTION__, devId);
    return 1;
}

extern "C" JNIEXPORT jint JNICALL
nativeHangup(JNIEnv *env, jobject obj, jstring jdevId) {
    char *devId = (NULL != jdevId) ? (char *) env->GetStringUTFChars(jdevId, NULL) : NULL;
    LOGD("%s: devId: %s", __FUNCTION__, devId);
    rinoEventCallback(1, 2, "hangup");
    return 1;
}

//初始化函数,注册native和java方法
extern "C" JNIEXPORT jint JNICALL
nativeInitClass(JNIEnv *env, jobject obj) {
    LOGD("%s:", __FUNCTION__);
    rinoCallManagerClassInfo.globalObject = env->NewGlobalRef(obj);
    rinoCallManagerClassInfo.weakObject = env->NewWeakGlobalRef(obj);
    if (NULL == rinoCallManagerClassInfo.globalObject) {
        LOGD("%s: failed!", __FUNCTION__);
        env->DeleteLocalRef(obj);
        return JNI_ERR;
    }
    return JNI_OK;
}

/**
 * native对应注册方法,需要再次扩充相关方法
 */
static const JNINativeMethod jniNativeMethod[] =
        {
                {"nativeInitClass",    "()I",                    (void *) (nativeInitClass)},
                {"takeInvite",         "(Ljava/lang/String;Z)I", (void *) (nativeTakeInvite)},
                {"registerCallModule", "(Ljava/lang/String;)I",  (void *) (nativeRegisterCallModule)},
                {"notifyAcceptInvite", "(Ljava/lang/String;)I",  (void *) (nativenotifyAcceptInvite)},
                {"notifyRingBack",     "(Ljava/lang/String;)I",  (void *) (nativeNotifyRingBack)},
                {"hangup",             "(Ljava/lang/String;)I",  (void *) (nativeHangup)},
        };

/**
 *回调Java上层方法
 */
static jniMethod_t jniJavaMethods[] =
        {
                {classPathName, "rinoEventCallback", "(IILjava/lang/String;)V",
                 &rinoCallManagerClassInfo.rinoEventCallback}
        };

/**
 * jni方法的绑定
 */
jint jniFindMethods(JNIEnv *env, jniMethod_t *methods, int count) {
    LOGD("%s:", __FUNCTION__);
    int i = 0;
    if (NULL == env) {
        LOGE("%s: JNIEnv is null.", __FUNCTION__);
        return JNI_ERR;
    }

    if (NULL == methods) {
        LOGE("%s: jniMethod_t is null.", __FUNCTION__);
        return JNI_ERR;
    }

    for (i = 0; i < count; i++) {
        jniMethod_t *f = &methods[i];
        jclass clazz = env->FindClass(f->className);
        if (NULL == clazz) {
            LOGE("%s: Can't find %s", __FUNCTION__, f->className);
            return JNI_ERR;
        }

        jmethodID method = env->GetMethodID(clazz, f->methodName, f->methodType);
        if (NULL == method) {
            LOGE("%s: Can't find %s.%s", __FUNCTION__, f->className, f->methodName);
            return JNI_ERR;
        }
        *(f->jmethod) = method;
        env->DeleteLocalRef(clazz);
    }
    return JNI_OK;
}

/**
 *开始注册native接口
 */
jint registerNativeMethod(JNIEnv *env) {
    jclass rinoCallManagerClass = env->FindClass(classPathName);
    env->RegisterNatives(rinoCallManagerClass, jniNativeMethod,
                         sizeof(jniNativeMethod) / sizeof(JNINativeMethod));//动态注册的数量
    jniFindMethods(env, jniJavaMethods, NELEM(jniJavaMethods));
    if (NULL == rinoCallManagerClass) {
        env->DeleteLocalRef(rinoCallManagerClass);
        return JNI_ERR;
    }
    return JNI_OK;
}

void setJavaVM(JavaVM *vm) {
    gJavaVM = vm;
}

/**
 * 这个函数会在库被加载的时候被调用
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    LOGD("%s:", __FUNCTION__);
    JNIEnv *jniEnv = get_env(vm, 0);
    if (jniEnv == NULL) {
        LOGE("%s: JNIEnv is NULL.", __FUNCTION__);
        return JNI_ERR;
    }
    setJavaVM(vm);
    registerNativeMethod(jniEnv);
    return JNI_VERSION_1_6;
}